package com.cskaoyan.wordmemorize.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.cskaoyan.wordmemorize.common.constant.ResultCodeEnum;
import com.cskaoyan.wordmemorize.common.exception.BusinessException;
import com.cskaoyan.wordmemorize.converter.UserConverter;
import com.cskaoyan.wordmemorize.dao.entity.*;
import com.cskaoyan.wordmemorize.dao.mapper.*;
import com.cskaoyan.wordmemorize.dto.admin.PageDTO;
import com.cskaoyan.wordmemorize.dto.app.*;
import com.cskaoyan.wordmemorize.request.PageRequest;
import com.cskaoyan.wordmemorize.request.UserRemindCommand;
import com.cskaoyan.wordmemorize.service.UserService;
import com.cskaoyan.wordmemorize.sms.SmsDelayMessage;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.DelayQueue;
import java.util.stream.Collectors;

@Service
@Slf4j
public class UserServiceImpl implements UserService {

    @Resource
    UserMapper userMapper;

    @Resource
    UserConverter userConverter;

    @Resource
    UserVocBoundLogMapper userVocBoundLogMapper;

    @Resource
    UserRemindMapper userRemindMapper;

    @Resource
    UserCheckinStatisticsMapper userCheckinStatisticsMapper;
    @Resource
    LevelMapper levelMapper;

    @Resource
    LevelPrivilegeMapper levelPrivilegeMapper;

    @Autowired
    UserVocBoundStatisticsMapper userVocBoundStatisticsMapper;

    @Autowired
    DelayQueue<SmsDelayMessage> delayQueue;

    @Autowired
    UserCheckinMapper userCheckinMapper;

    @Value("${user.remind.description}")
    String messageRemindDescription;

    /**
     * APP端获取用户信息
     *
     * @param userId
     * @return
     */
    @Override
    public UserDTO getUserInfo(Long userId) {
        // 1. 查询用户基本信息
        UserDO userDO = userMapper.selectById(userId);
        if (userDO == null) {
            throw new BusinessException(ResultCodeEnum.FAIL);
        }
        // 2. 查询等级信息
        Integer levelValue = userDO.getLevelValue();
        LevelDO levelDO = null;
        if (levelValue != null) {
            levelDO = levelMapper.selectOne(
                    new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<LevelDO>()
                            .eq(LevelDO::getLevelValue, levelValue)
                            .eq(LevelDO::getIsDeleted, 0));
        }
        // 3. 查询等级特权集合
        List<String> privilegeCodes = new java.util.ArrayList<>();
        if (levelDO != null) {
            List<LevelPrivilegeDO> privilegeDOList = levelPrivilegeMapper.selectList(
                    new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<LevelPrivilegeDO>()
                            .eq(LevelPrivilegeDO::getLevelId, levelDO.getId())
                            .eq(LevelPrivilegeDO::getIsDeleted, 0));
            privilegeCodes = privilegeDOList.stream()
                    .map(LevelPrivilegeDO::getPrivilegeCode)
                    .filter(java.util.Objects::nonNull)
                    .toList();
        }
        // 4. 组装DTO
        UserDTO userDTO = userConverter.userDO2DTO(userDO);
        userDTO.setPrivilegeCodes(privilegeCodes);
        return userDTO;
    }

    /**
     * APP端获取用户带升级信息
     *
     * @param userId
     * @return
     */
    @Override
    public NextLevelDTO getUserNextDTO(Long userId) {

        // 查询用户信息
        UserDO userDO = userMapper.selectById(userId);
        if (userDO == null) {
            return null;
        }
        Integer currentLevelValue = userDO.getLevelValue();
        if (currentLevelValue == null) {
            currentLevelValue = 0;
        }
        // 查询当前等级信息
        LevelDO nowLevel = levelMapper.selectOne(
                new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<LevelDO>()
                        .eq(LevelDO::getLevelValue, currentLevelValue)
                        .eq(LevelDO::getIsDeleted, 0));
        // 查询下一级等级信息
        LevelDO nextLevel = levelMapper.selectOne(
                new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<LevelDO>()
                        .eq(LevelDO::getLevelValue, currentLevelValue + 1)
                        .eq(LevelDO::getIsDeleted, 0));
        NextLevelDTO dto = new NextLevelDTO();
        if (nextLevel == null) {
            // 已经满级
            dto.setSignType(0);
            dto.setRemainDays(0);
            return dto;
        }

        // 获取用户等级更新时间
        LocalDate levelDate = userDO.getLevelDate();
        // 查询等级更新日期之后的签到记录条数
        LambdaQueryWrapper<UserCheckinDO> checkinDOLambdaQueryWrapper = new LambdaQueryWrapper<>();
        checkinDOLambdaQueryWrapper.eq(UserCheckinDO::getUserId, userId)
                .gt(UserCheckinDO::getSignDate, levelDate);
        List<UserCheckinDO> userCheckinDOS = userCheckinMapper.selectList(checkinDOLambdaQueryWrapper);
        int checkinDays = userCheckinDOS.size();

        // 查询签到统计
        UserCheckinStatisticsDO stats = userCheckinStatisticsMapper.selectOne(
                new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<UserCheckinStatisticsDO>()
                        .eq(UserCheckinStatisticsDO::getUserId, userId)
                        .eq(UserCheckinStatisticsDO::getIsDeleted, 0));
        int remainDays = 0;
        int signType = nextLevel.getCheckInType();
        dto.setSignType(signType);
        if (stats == null) {
            // 没有签到统计，说明还未签到
            remainDays = nextLevel.getCheckInDays() - nowLevel.getCheckInDays();
        } else {
            if (signType == 1) {
                // 连续签到
                int curContinuousDays = stats.getCurContinuousDays() == null ? 0 : stats.getCurContinuousDays();
                remainDays = nextLevel.getCheckInDays() - curContinuousDays;
            } else {
                // 累计签到
                // int totalDays = stats.getTotalDays() == null ? 0 : stats.getTotalDays();
                remainDays = nextLevel.getCheckInDays() - nowLevel.getCheckInDays() - checkinDays;
            }
            if (remainDays < 0)
                remainDays = 0;
        }
        dto.setRemainDays(remainDays);
        return dto;
    }

    // 获取用户等级信息（已解锁等级和未解锁等级）
    @Override
    public DisplayUserLevelDTO getUserLevelInfo(Long userId) {

        // 查询用户当前等级
        LevelDO currentLevel = levelMapper.selectCurrentLevel(userId);
        if (currentLevel == null) {
            currentLevel = new LevelDO();
            currentLevel.setId(0L); // 未设置等级，默认初始值为0
            currentLevel.setLevelName("LVL0");
            currentLevel.setLevelValue(0);
            currentLevel.setCheckInDays(0);
            currentLevel.setCheckInType(2);
            currentLevel.setUnlockPrice(BigDecimal.ZERO);
        }

        // 查询当前等级的特权
        List<LevelPrivilegeDO> currentPrivileges = levelMapper
                .selectPrivilegesByLevelId(currentLevel.getId().toString());

        // 查询更高等级（未解锁等级）
        List<LevelDO> upperLevels = levelMapper
                .selectUpperLevels(currentLevel.getLevelValue());

        // 组装当前等级DTO
        LevelPageDTO currentLevelDTO = userConverter.levelDO2LevelPageDTO(currentLevel);
        currentLevelDTO.setLevelPrivileges(currentPrivileges.stream()
                .map(userConverter::levelPrivilegeDO2DTO)
                .collect(Collectors.toList()));

        // 组装更高等级DTO（包含各自特权）
        List<LevelPageDTO> upperLevelDTOs = upperLevels.stream()
                .map(level -> {
                    List<LevelPrivilegeDO> privileges = levelMapper
                            .selectPrivilegesByLevelId(level.getId().toString());
                    LevelPageDTO dto = userConverter.levelDO2LevelPageDTO(level);
                    dto.setLevelPrivileges(privileges.stream()
                            .map(userConverter::levelPrivilegeDO2DTO)
                            .collect(Collectors.toList()));
                    return dto;
                })
                .collect(Collectors.toList());

        // 封装最终返回结果
        DisplayUserLevelDTO result = new DisplayUserLevelDTO();
        result.setNowLevel(Collections.singletonList(currentLevelDTO)); // 当前等级用列表包装
        result.setUpperLevel(upperLevelDTOs); // 更高等级列表

        return result;
    }

    /**
     * 更新用户每日学习量
     *
     * @param userId
     * @param count
     */
    @Override
    public void updateVocCountOfDay(Long userId, Integer count) {
        if (count < 0) {
            throw new RuntimeException("单词数量不能小于0");
        }

        // 查询用户是否存在
        if (userId == null) {
            throw new RuntimeException("用户不存着");
        }

        // 修改数据并加入数据库
        UserDO userDO = userMapper.selectById(userId);
        userDO.setVocCountOfDay(count);
        userMapper.updateById(userDO);
    }

    // 更新App端用户的学习提醒时间
    @Override
    public void updateUserRemind(UserRemindCommand command, Long userId) {
        // 检查用户是否存在
        if (userId == null || userMapper.selectById(userId) == null) {
            throw new RuntimeException("用户不存在");
        }

        if (command.getRemindTime() == null || command.getRemindTime().isEmpty()) {
            command.setRemindTime("19:30"); // 设置默认值为19:30
        }

        // 构造DO
        UserRemindDO userRemindDO = userConverter.userRemindCommand2DO(command);
        userRemindDO.setUserId(userId); // 强制使用当前登录用户id

        // 查询现有记录
        LambdaQueryWrapper<UserRemindDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserRemindDO::getUserId, userId);
        UserRemindDO existingRemind = userRemindMapper.selectOne(queryWrapper);

        if (existingRemind != null) {
            userRemindDO.setId(existingRemind.getId());
            userRemindMapper.updateById(userRemindDO);
        } else {
            userRemindMapper.insert(userRemindDO);
        }
    }

    /**
     * 获取用户的提醒信息
     *
     * @param userId
     * @return
     */
    @Override
    public UserRemindDTO getUserRemind(Long userId) {
        if (userId == null || userMapper.selectById(userId) == null) {
            throw new RuntimeException("用户不存在");
        }
        // 查user_remind表
        LambdaQueryWrapper<UserRemindDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserRemindDO::getUserId, userId);
        UserRemindDO remindDO = userRemindMapper.selectOne(queryWrapper);
        UserRemindDTO dto = new UserRemindDTO();
        if (remindDO != null) {
            dto.setId(remindDO.getId() == null ? null : remindDO.getId().toString());
            dto.setUserId(remindDO.getUserId() == null ? null : remindDO.getUserId().toString());
            dto.setRemindTime(remindDO.getRemindTime() == null ? "19:30" : remindDO.getRemindTime().toString());
            dto.setMessageStatus(remindDO.getMessageStatus());
            dto.setMessageRemindDescription(messageRemindDescription);
        } else {
            // 查不到返回默认
            dto.setRemindTime("19:30");
            dto.setMessageStatus(1);
            dto.setMessageRemindDescription(messageRemindDescription);
        }
        return dto;
    }

    /**
     * 消息提醒
     */
    @Override
    public void messageRemind() {
        LocalTime now = LocalTime.now();
        LocalTime oneHourLater = now.plusHours(1);

        // 循环+分页查询未来一小时内需要提醒的用户
        int page = 1;
        int pageSize = 100;
        List<UserRemindDTO> userReminds;
        do {
            userReminds = userRemindMapper.getUsersToRemind(now, oneHourLater, page, pageSize);
            for (UserRemindDTO userRemind : userReminds) {
                Long userId = Long.valueOf(userRemind.getUserId());
                LocalTime remindTime = LocalTime.parse(userRemind.getRemindTime());

                // 判断用户今天是否尚未签到
                QueryWrapper<UserCheckinDO> checkinDOQueryWrapper = new QueryWrapper<>();
                checkinDOQueryWrapper.eq("user_id", userId)
                        .orderByDesc("sign_date")
                        .last("limit 1");
                UserCheckinDO recentUserCheckinDO = userCheckinMapper.selectOne(checkinDOQueryWrapper);
                boolean isNotCheckedInToday = recentUserCheckinDO.getSignDate().isEqual(LocalDate.now());

                if (isNotCheckedInToday) {
                    // 生成待发送的短信消息
                    SmsDelayMessage smsDelayMessage = new SmsDelayMessage();
                    smsDelayMessage.setUserId(userId);
                    smsDelayMessage.setRemindTime(remindTime);

                    // 放入延迟队列
                    delayQueue.add(smsDelayMessage);
                }
            }
            page++;
        } while (userReminds.size() == pageSize);
    }

    // 单词上限下半部分
    @Override
    public PageDTO<DisplayUserVocBoundLogDTO> getUserVocBoundLog(PageRequest pageRequest, Long userId) {

        if (userId == null) {
            throw new BusinessException(ResultCodeEnum.FAIL);
        }
        // 构建分页查询条件
        com.baomidou.mybatisplus.extension.plugins.pagination.Page<UserVocBoundLogDO> page = new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(
                pageRequest.getPageNum() == null ? 1 : pageRequest.getPageNum(),
                pageRequest.getCount() == null ? 10 : pageRequest.getCount());
        // 查询数据库
        LambdaQueryWrapper<UserVocBoundLogDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserVocBoundLogDO::getUserId, userId)
                .orderByDesc(UserVocBoundLogDO::getLogDate/* , UserVocBoundLogDO::getType */);

        // 执行分页查询
        IPage<UserVocBoundLogDO> resultPage = userVocBoundLogMapper.selectPage(page, queryWrapper);

        // 转换结果
        List<DisplayUserVocBoundLogDTO> displayDTOs = userConverter
                .userVocBoundLogDTOs2DisplayDTOs(resultPage.getRecords());

        // 返回分页结果
        return userConverter.userVocBoundLogPage2PageDTO(displayDTOs, resultPage.getTotal());
    }

    // 单词上限上半部分
    @Override
    public UserVocBoundStatisticsDTO getUserVocBoundStatistics(Long userId) {

        // 构建查询条件：按用户ID查询统计数据
        LambdaQueryWrapper<UserVocBoundStatisticsDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserVocBoundStatisticsDO::getUserId, userId);

        // 从数据库查询数据（若用户未生成统计数据，返回null）
        UserVocBoundStatisticsDO statisticsDO = userVocBoundStatisticsMapper.selectOne(queryWrapper);

        // 若数据不存在，初始化默认DO（避免空指针，所有数值默认为0）
        if (statisticsDO == null) {
            statisticsDO = UserVocBoundStatisticsDO.builder()
                    .userId(userId)
                    .pay(0)
                    .free(0)
                    .exchange(0)
                    .total(0)
                    .occupied(0)
                    .available(0)
                    .build();
        }

        // 通过转换器将DO转换为DTO（返回给前端）
        return userConverter.userVocBoundStatisticsDO2DisplayDTO(statisticsDO);
    }

    /**
     * APP端获取用户页面信息
     *
     * @param userId
     * @return
     */
    @Override
    public UserPageDTO getUserPage(Long userId) {

        // 1. 获取用户基本信息
        UserDO userDO = userMapper.selectById(userId);
        if (userDO == null) {
            throw new BusinessException(ResultCodeEnum.FAIL);
        }

        UserPageDTO userPageDTO = new UserPageDTO();

        // 获取每日学习量
        userPageDTO.setVocCountOfDay(userDO.getVocCountOfDay() == null ? 20 : userDO.getVocCountOfDay());

        // 获取免费获取单词上限
        Integer freeVocBound = userVocBoundStatisticsMapper.sumFreeVocBound(userId);
        userPageDTO.setFreeVocBound(freeVocBound == null ? 0 : freeVocBound);
        // 获取购买单词上限
        Integer payVocBound = userVocBoundStatisticsMapper.sumPayVocBound(userId);
        userPageDTO.setPayVocBound(payVocBound == null ? 0 : payVocBound);
        // 获取现单词上限总量
        Integer totalVocBound = userVocBoundStatisticsMapper.totalVocBound(userId);
        userPageDTO.setTotalVocBound(totalVocBound);
        // 获取学习提醒时间
        userPageDTO.setRemindTime(getUserRemind(userId).getRemindTime());
        // 获取剩余补签卡的数量
        userPageDTO.setReSignCard(userDO.getHasSignCard());

        return userPageDTO;
    }

}
